package com.annchain.og_browser.service.impl;

import com.annchain.og_browser.entity.*;
import com.annchain.og_browser.mapper.*;
import com.annchain.og_browser.service.KnownAddressService;
import com.annchain.og_browser.service.OgbTransactionService;
import com.annchain.og_browser.util.EmojiUtil;
import com.annchain.og_browser.util.TimeUtil;
import com.annchain.og_browser.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.gson.Gson;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.*;

@Service
public class OgbTransactionServiceImpl implements OgbTransactionService {
    private static final Logger logger = LoggerFactory.getLogger(OgbTransactionServiceImpl.class);

    @Autowired
    private OgbTransactionMapper ogbTransactionMapper;
    @Autowired
    private OgbKnowAssetsMapper ogbKnowAssetsMapper;
    @Autowired
    private OgbBlockMapper ogbBlockMapper;
    @Autowired
    private OgbTxRelationMapper ogbTxRelationMapper;
    @Autowired
    private OgbAttachmentMapper ogbAttachmentMapper;
    @Autowired
    private KnownAddressService knownAddressService;

    @Override
    @ApiOperation("交易列表分页")
    public BasePageResponse<LatestTransactionVO> findData(Integer page, Integer limit) {
        BasePageResponse pageResponse = new BasePageResponse();
        IPage<OgbTransaction> ogbTransactionIPage = new Page<>(page, limit);
        ((Page<OgbTransaction>) ogbTransactionIPage).setOptimizeCountSql(true);
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.orderByDesc("height");
        IPage<OgbTransaction> result = ogbTransactionMapper.selectPage(ogbTransactionIPage, wrapper);
        if (result.getRecords().size() == 0) {
            return null;
        }

        pageResponse.setTotal(result.getTotal());
        pageResponse.setPageNum(result.getCurrent());
        pageResponse.setPageSize(result.getSize());
        List<OgbTransaction> ogbTransactionList = result.getRecords();
        List<LatestTransactionVO> latestTransactionVOList = new ArrayList<>();
        for (OgbTransaction ogbTransaction : ogbTransactionList) {
            LatestTransactionVO latestTransactionVO = new LatestTransactionVO();
            BeanUtils.copyProperties(ogbTransaction, latestTransactionVO);

            switch (ogbTransaction.getType()) {
                case 0:
                    latestTransactionVO.setType("转账");
                    break;
                case 1:
                    latestTransactionVO.setType("序列块");
                    break;
                case 2:
                    latestTransactionVO.setType("竞选");
                    break;
                case 3:
                    latestTransactionVO.setType("换届");
                    break;
                case 4:
                    latestTransactionVO.setType("存证");
                    break;
                case 5:
                    latestTransactionVO.setType("发币");
                    break;
            }

            latestTransactionVO.setFrom(knownAddressService.getAddress(ogbTransaction.getFromAddress()));
            latestTransactionVO.setTo(knownAddressService.getAddress(ogbTransaction.getToAddress()));

            latestTransactionVOList.add(latestTransactionVO);
            //利用工具类计算时间差

            if (ogbTransaction.getOutputTime() != null) {
                Date curTime = new Date();
                Date outputTime = ogbTransaction.getOutputTime();

                latestTransactionVO.setOutputTime(outputTime.getTime());
                latestTransactionVO.setTimeDiff(curTime.getTime() - outputTime.getTime());
//                latestTransactionVO.setTimeDiff(ogbTransaction.getOutputTime().getTime());
            }

        }
        pageResponse.setData(latestTransactionVOList);
        return pageResponse;
    }


    @Override
    @ApiOperation("通过哈希查找转账交易")
    public TransferTxVO findTransferByTxHash(String hash) {
        TransferTxVO transferTxVO = new TransferTxVO();
        //根据hash查找交易
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("hash", hash);
        OgbTransaction ogbTransaction = ogbTransactionMapper.selectOne(wrapper);

        if (ogbTransaction == null) {
            return null;
        }
        BeanUtils.copyProperties(ogbTransaction, transferTxVO);
        transferTxVO.setType("转账");
        //时间转化标准类型
        transferTxVO.setOutputTime(TimeUtil.TimeStampToString(ogbTransaction.getOutputTime()));


        //获取资产类型名称
        wrapper = new QueryWrapper();
        wrapper.eq("property_type", ogbTransaction.getPropertyType());
        OgbKnownAssets ogbKnownAssets = ogbKnowAssetsMapper.selectOne(wrapper);
        if (ogbKnownAssets != null) {
            transferTxVO.setPropertyTypeName(ogbKnownAssets.getName());
        }

        //根据交易高度查找区块
        wrapper = new QueryWrapper();
        wrapper.eq("height", ogbTransaction.getHeight());
        OgbBlock ogbBlock = ogbBlockMapper.selectOne(wrapper);
        wrapper.clear();
        if (ogbBlock == null) {
            return null;
        }

        //根据区块miner从地址表查找名字
        transferTxVO.setMiner(knownAddressService.getAddress(ogbBlock.getMiner()));
        transferTxVO.setFrom(knownAddressService.getAddress(ogbTransaction.getFromAddress()));
        transferTxVO.setTo(knownAddressService.getAddress(ogbTransaction.getToAddress()));

        //添加父节点列表
        wrapper = new QueryWrapper();
        wrapper.eq("hash", ogbTransaction.getHash());
        List<OgbTxRelation> ogbTxRelationList = new ArrayList<>();
        ogbTxRelationList = ogbTxRelationMapper.selectList(wrapper);
        List<String> list = new ArrayList<>();  //存放父节点的列表
        if (ogbTxRelationList.size() != 0) {
            for (OgbTxRelation ogbTxRelation : ogbTxRelationList) {
                list.add(ogbTxRelation.getParentHash());
            }
        }
        transferTxVO.setParentList(list);
        return transferTxVO;
    }

    @Override
    @ApiOperation("通过哈希查找存证交易")
    public DepositeTxVO findDepositeByTxHash(String hash) {
        DepositeTxVO depositeTxVO = new DepositeTxVO();

        //根据hash查找交易
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("hash", hash);
        OgbTransaction ogbTransaction = ogbTransactionMapper.selectOne(wrapper);
        if (ogbTransaction == null) {
            return null;
        }
        BeanUtils.copyProperties(ogbTransaction, depositeTxVO);
        depositeTxVO.setType("存证");
        //时间准换标准类型
        depositeTxVO.setOutputTime(TimeUtil.TimeStampToString(ogbTransaction.getOutputTime()));

        //根据交易高度查找区块
        wrapper = new QueryWrapper();
        wrapper.eq("height", ogbTransaction.getHeight());
        OgbBlock ogbBlock = ogbBlockMapper.selectOne(wrapper);

        if (ogbBlock == null) {
            return null;
        }

        //根据区块miner从地址表查找名字
        depositeTxVO.setMiner(knownAddressService.getAddress(ogbBlock.getMiner()));
        depositeTxVO.setFrom(knownAddressService.getAddress(ogbTransaction.getFromAddress()));
        depositeTxVO.setTo(knownAddressService.getAddress(ogbTransaction.getToAddress()));

        //添加父节点列表
        wrapper = new QueryWrapper();
        wrapper.eq("hash", ogbTransaction.getHash());
        List<OgbTxRelation> ogbTxRelationList = ogbTxRelationMapper.selectList(wrapper);
        List<String> list = new ArrayList<>();  //存放父节点的列表
        if (ogbTxRelationList.size() != 0) {
            for (OgbTxRelation ogbTxRelation : ogbTxRelationList) {
                list.add(ogbTxRelation.getParentHash());
            }
        }
        depositeTxVO.setParentList(list);

        //添加存证信息
        wrapper = new QueryWrapper();
        wrapper.eq("id", ogbTransaction.getDataId());
        OgbAttachment ogbAttachment = ogbAttachmentMapper.selectOne(wrapper);
        if (ogbAttachment != null) {
            try {
                depositeTxVO.setData(EmojiUtil.filter(new String(Base64.getDecoder().decode(ogbAttachment.getData()), StandardCharsets.UTF_8)));
            } catch (Exception e) {
                depositeTxVO.setData(ogbAttachment.getData());
            }

        }
        return depositeTxVO;
    }

    @Override
    public IssueTxVO findIssueByTxHash(String hash) {
        IssueTxVO issueTxVO = new IssueTxVO();
        //根据hash查找交易
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("hash", hash);
        OgbTransaction ogbTransaction = ogbTransactionMapper.selectOne(wrapper);
        if (ogbTransaction == null) {
            return null;
        }
        BeanUtils.copyProperties(ogbTransaction, issueTxVO);
        issueTxVO.setType("发币");
        //时间转化标准类型
        TimeUtil timeUtil = new TimeUtil();
        issueTxVO.setOutputTime(timeUtil.TimeStampToString(ogbTransaction.getOutputTime()));

        //获取资产类型名称
        wrapper = new QueryWrapper();
        wrapper.eq("property_type", ogbTransaction.getPropertyType());
        OgbKnownAssets ogbKnownAssets = ogbKnowAssetsMapper.selectOne(wrapper);
        if (ogbKnownAssets != null) {
            issueTxVO.setPropertyTypeName(ogbKnownAssets.getName());
        }

        //根据交易高度查找区块
        wrapper = new QueryWrapper();
        wrapper.eq("height", ogbTransaction.getHeight());
        OgbBlock ogbBlock = ogbBlockMapper.selectOne(wrapper);
        wrapper.clear();
        if (ogbBlock == null) {
            return null;
        }

        //根据区块miner从地址表查找名字
        issueTxVO.setMiner(knownAddressService.getAddress(ogbBlock.getMiner()));
        issueTxVO.setFrom(knownAddressService.getAddress(ogbTransaction.getFromAddress()));
        issueTxVO.setTo(knownAddressService.getAddress(ogbTransaction.getToAddress()));


        //添加父节点列表
        wrapper = new QueryWrapper();
        wrapper.eq("hash", ogbTransaction.getHash());
        List<OgbTxRelation> ogbTxRelationList = new ArrayList<>();
        ogbTxRelationList = ogbTxRelationMapper.selectList(wrapper);
        List<String> list = new ArrayList<>();  //存放父节点的列表
        if (ogbTxRelationList.size() != 0) {
            for (OgbTxRelation ogbTxRelation : ogbTxRelationList) {
                list.add(ogbTxRelation.getParentHash());
            }
        }
        issueTxVO.setParentList(list);

        //添加发币信息
        wrapper = new QueryWrapper();
        wrapper.eq("id", ogbTransaction.getDataId());
        OgbAttachment ogbAttachment = ogbAttachmentMapper.selectOne(wrapper);
        Map<String, Object> map = new HashMap<String, Object>();
        Gson gson = new Gson();
        if (ogbAttachment != null) {
            map = gson.fromJson(ogbAttachment.getData(), map.getClass());
            issueTxVO.setData(map);
        }
        return issueTxVO;
    }

    @Override
    @ApiOperation("通过区块高度查找所含交易分页列表")
    public BasePageResponse<List<TransactionsInBlock>> findTibByHeight(Integer page, Integer limit, Integer height) {
        BasePageResponse pageResponse = new BasePageResponse();
        IPage<OgbTransaction> ogbTransactionIPage = new Page<>(page, limit);
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("height", height);
        IPage<OgbTransaction> result = ogbTransactionMapper.selectPage(ogbTransactionIPage, wrapper);
        if (result.getRecords().size() == 0) {
            return null;
        }
        pageResponse.setTotal(result.getTotal());
        pageResponse.setPageNum(result.getCurrent());
        pageResponse.setPageSize(result.getSize());
        List<OgbTransaction> ogbTransactionList = result.getRecords();
        List<TransactionsInBlock> transactionsInBlockList = new ArrayList<>();
        for (OgbTransaction ogbTransaction : ogbTransactionList) {
            TransactionsInBlock transactionsInBlock = getTransactionsInBlock(ogbTransaction);
            transactionsInBlockList.add(transactionsInBlock);
        }
        pageResponse.setData(transactionsInBlockList);
        return pageResponse;
    }

    private TransactionsInBlock getTransactionsInBlock(OgbTransaction ogbTransaction) {
        QueryWrapper wrapper;
        TransactionsInBlock transactionsInBlock = new TransactionsInBlock();

        if (ogbTransaction != null) {
            transactionsInBlock.setHash(ogbTransaction.getHash());
            //时间准换标准类型
            transactionsInBlock.setOutputTime(TimeUtil.TimeStampToString(ogbTransaction.getOutputTime()));
            transactionsInBlock.setDataSize(ogbTransaction.getDataSize());
            transactionsInBlock.setAmount(ogbTransaction.getAmount());
            transactionsInBlock.setFrom(knownAddressService.getAddress(ogbTransaction.getFromAddress()));
            transactionsInBlock.setTo(knownAddressService.getAddress(ogbTransaction.getToAddress()));

            //交易类型名称
            wrapper = new QueryWrapper();
            wrapper.eq("property_type", ogbTransaction.getPropertyType());
            OgbKnownAssets ogbKnownAssets = new OgbKnownAssets();
            ogbKnownAssets = ogbKnowAssetsMapper.selectOne(wrapper);
            if (ogbKnownAssets != null) {
                transactionsInBlock.setName(ogbKnownAssets.getName());
            }
        }

        switch (ogbTransaction.getType()) {
            case 0:
                transactionsInBlock.setType("转账");
                break;
            case 1:
                transactionsInBlock.setType("序列块");
                break;
            case 2:
                transactionsInBlock.setType("竞选");
                break;
            case 3:
                transactionsInBlock.setType("换届");
                break;
            case 4:
                transactionsInBlock.setType("存证");
                break;
            case 5:
                transactionsInBlock.setType("发币");
                wrapper = new QueryWrapper();
                wrapper.eq("id", ogbTransaction.getDataId());
                OgbAttachment ogbAttachment = ogbAttachmentMapper.selectOne(wrapper);
                boolean flag = false;
                if (ogbAttachment != null) {
                    flag = ogbAttachment.getData().contains("true");
                }
                if (flag == true)
                    transactionsInBlock.setEnableSpo("可增发");
                else
                    transactionsInBlock.setEnableSpo("不可增发");
                break;
        }
        return transactionsInBlock;
    }

    @Override
    @ApiOperation("通过区块hash查找所含交易分页列表")
    public BasePageResponse<List<TransactionsInBlock>> findTibByHash(Integer page, Integer limit, String hash) {
        BasePageResponse pageResponse = new BasePageResponse();
        IPage<OgbTransaction> ogbTransactionIPage = new Page<>(page, limit);
        //通过区块哈希查区块高度
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("hash", hash);
        OgbBlock ogbBlock = ogbBlockMapper.selectOne(wrapper);
        if (ogbBlock == null) {
            logger.info("不存在该区块");
            return null;
        }
        //通过区块高度查交易
        wrapper = new QueryWrapper();
        wrapper.eq("height", ogbBlock.getHeight());
        IPage<OgbTransaction> result = ogbTransactionMapper.selectPage(ogbTransactionIPage, wrapper);

        if (result.getRecords().size() == 0) {
            return null;
        }
        pageResponse.setTotal(result.getTotal());
        pageResponse.setPageNum(result.getCurrent());
        pageResponse.setPageSize(result.getSize());
        List<OgbTransaction> ogbTransactionList = result.getRecords();
        List<TransactionsInBlock> transactionsInBlockList = new ArrayList<>();
        for (OgbTransaction ogbTransaction : ogbTransactionList) {
            TransactionsInBlock transactionsInBlock = getTransactionsInBlock(ogbTransaction);
            transactionsInBlockList.add(transactionsInBlock);
        }
        pageResponse.setData(transactionsInBlockList);
        return pageResponse;
    }

    @Override
    public OgbTransaction findByHash(String hash) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("hash", hash);
        OgbTransaction ogbTransaction = ogbTransactionMapper.selectOne(wrapper);
        return ogbTransaction;
    }

}
